﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace SharpSoft.WPF.Controls
{
    /// <summary>
    /// 运动容器（容器内的元素可以指定移动速度）
    /// </summary>
    public class MovementCanvas : Canvas
    {
        private readonly TimeTracker _timeTracker;
        /// <summary>
        /// 记录子元素的即时速度
        /// </summary>
        private readonly Dictionary<UIElement, Vector> _velocities = new Dictionary<UIElement, Vector>();

        public MovementCanvas()
        {
            this._timeTracker = new TimeTracker();
            this._velocities = new Dictionary<UIElement, Vector>();
            this.Loaded += MovementCanvas_Loaded;
            this.Unloaded += MovementCanvas_Unloaded;
        }

        private void MovementCanvas_Unloaded(object sender, RoutedEventArgs e)
        {
            if (!DesignerProperties.GetIsInDesignMode(this))
            {
                CompositionTarget.Rendering -= CompositionTarget_Rendering;
            }
        }

        private void MovementCanvas_Loaded(object sender, RoutedEventArgs e)
        {
            if (!DesignerProperties.GetIsInDesignMode(this))
            {
                CompositionTarget.Rendering += CompositionTarget_Rendering;
            }
        }


        public event RoutedEventHandler ChildrenUpdated
        {
            add { AddHandler(ChildrenUpdatedEvent, value); }
            remove { RemoveHandler(ChildrenUpdatedEvent, value); }
        }

        public static readonly RoutedEvent ChildrenUpdatedEvent = EventManager.RegisterRoutedEvent(
        "ChildrenUpdated", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MovementCanvas));

        protected virtual void OnChildrenUpdated()
        {
            RoutedEventArgs args = new RoutedEventArgs();
            args.RoutedEvent = MovementCanvas.ChildrenUpdatedEvent;
            this.RaiseEvent(args);
        }

        #region handlers
        private void _timeTracker_TimerFired(object sender, TimerFiredEventArgs e)
        {
        }
        int counter = 0;
        private void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            if (counter % 2 == 0)
            {
                counter = 0;
                var ds = _timeTracker.Update();
                UpdateChildren(ds);
                OnChildrenUpdated();
            }
            counter++;
        }
        #endregion

        #region 实现
        /// <summary>
        /// 更新子项的状态
        /// </summary>
        /// <param name="ds"></param>
        private void UpdateChildren(double ds)
        {
            var children = LogicalTreeHelper.GetChildren(this);
            var aw = this.ActualWidth;
            var ah = this.ActualHeight;
            switch (this.EdgeBehavior)
            {
                case EdgeBehavior.Bounce://反弹
                    foreach (UIElement item in children)
                    {
                        if (GetIsPaused(item))
                        {
                            continue;
                        }
                        var bound = GetRealBounds(item);
                        if (bound.Width == 0 || bound.Height == 0)
                        {//不显示或尚未渲染
                            continue;
                        }
                        var v = _velocities[item];
                        bool touchleft = bound.X <= 0, touchtop = bound.Y <= 0, touchright = bound.Right >= aw, touchbottom = bound.Bottom >= ah;
                        double vx = v.X, vy = v.Y;

                        if (touchleft && touchright)
                        {//同时碰触到左右边，x方向静止
                            vx = 0;
                        }
                        else
                        {
                            if (touchleft)
                            {
                                vx = Math.Abs(vx);
                            }
                            else if (touchright)
                            {
                                vx = -Math.Abs(vx);
                            }

                        }
                        if (touchbottom && touchtop)
                        {//同时碰触到上下边，y方向静止
                            vy = 0;
                        }
                        else
                        {
                            if (touchtop)
                            {
                                vy = Math.Abs(vy);
                            }
                            else if (touchbottom)
                            {
                                vy = -Math.Abs(vy);
                            }
                        }
                        v = new Vector(vx, vy);
                        var p = GetPosition(item);
                        SetPosition(item, p + (v * ds));
                        _velocities[item] = v;
                    }

                    break;
                case EdgeBehavior.Remove://移除
                    var container = new Rect(0, 0, aw, ah);
                    foreach (UIElement item in children)
                    {
                        if (GetIsPaused(item))
                        {
                            continue;
                        }
                        var bound = GetRealBounds(item);
                        if (bound.Width == 0 || bound.Height == 0)
                        {//不显示或尚未渲染
                            continue;
                        }
                        bool isoutSide = IsOutside(container, bound);
                        if (isoutSide)
                        {
                            this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background, new Action<UIElement>(RemoveChild), item);
                        }
                        var v = _velocities[item];

                        var p = GetPosition(item);
                        SetPosition(item, p + (v * ds));
                        _velocities[item] = v;
                    }
                    break;
                default:
                    break;
            }

        }
        /// <summary>
        /// 移除子项
        /// </summary>
        /// <param name="element"></param>
        private void RemoveChild(UIElement element)
        {
            this.Children.Remove(element);
        }
        /// <summary>
        /// 判断是否已超出容器边缘
        /// </summary>
        /// <param name="container"></param>
        /// <param name="ele"></param>
        /// <returns></returns>
        private bool IsOutside(Rect container, Rect ele)
        {
            bool result = true;

            if ((this.EdgeType & EdgeType.Top) == EdgeType.Top)
            {
                result &= (ele.Bottom < container.Top);
            }
            if ((this.EdgeType & EdgeType.Bottom) == EdgeType.Bottom)
            {
                result &= (ele.Top > container.Bottom);
            }
            if ((this.EdgeType & EdgeType.Left) == EdgeType.Left)
            {
                result &= (ele.Right < container.Left);
            }
            if ((this.EdgeType & EdgeType.Right) == EdgeType.Right)
            {
                result &= (ele.Left > container.Right);
            }
            return result;


            // return ele.Right < container.Left || ele.Bottom < container.Top || ele.Left > container.Right || ele.Top > container.Bottom;

        }
        /// <summary>
        /// 设置子项的位置
        /// </summary>
        /// <param name="element"></param>
        /// <param name="p"></param>
        public void SetPosition(UIElement element, Point p)
        {
            Canvas.SetLeft(element, double.IsNaN(p.X) ? 0 : p.X);
            Canvas.SetTop(element, double.IsNaN(p.Y) ? 0 : p.Y);
        }
        /// <summary>
        /// 获取子项的位置
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        public Point GetPosition(UIElement element)
        {
            var x = Canvas.GetLeft(element);
            var y = Canvas.GetTop(element);
            return new Point(double.IsNaN(x) ? 0 : x, double.IsNaN(y) ? 0 : y);
        }

        protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
        {
            base.OnVisualChildrenChanged(visualAdded, visualRemoved);
            if (visualAdded is UIElement uiele)
            {
                if (double.IsNaN(Canvas.GetLeft(uiele)))
                {
                    Canvas.SetLeft(uiele, 0d);
                }
                if (double.IsNaN(Canvas.GetTop(uiele)))
                {
                    Canvas.SetTop(uiele, 0d);
                }
                if (GetInitialVelocity(visualAdded) == null)
                {//没有指定初始速度
                    SetInitialVelocity(visualAdded, this.DefaultInitialVelocity);
                }
                this._velocities[uiele] = GetInitialVelocity(visualAdded).Value;
                //uiele.RenderTransform = new TranslateTransform();
            }
            if (visualRemoved is UIElement ue && this._velocities.ContainsKey(ue))
            {
                this._velocities.Remove(ue);
            }
        }
        /// <summary>
        /// 获取子项的范围
        /// </summary>
        /// <param name="uiele"></param>
        /// <returns></returns>
        private Rect GetRealBounds(UIElement uiele)
        {
            var p = uiele.TranslatePoint(new Point(), this);
            return new Rect(p, uiele.RenderSize);
        }

        /// <summary>
        /// 设置元素的即时速度
        /// </summary>
        /// <param name="ele"></param>
        /// <param name="vector"></param>
        public void SetVelocity(UIElement ele, Vector vector)
        {
            _velocities[ele] = vector;
        }
        /// <summary>
        /// 获取元素的即时速度
        /// </summary>
        /// <param name="ele"></param>
        /// <returns></returns>
        public Vector GetVelocity(UIElement ele)
        {
            if (_velocities.ContainsKey(ele))
            {
                return _velocities[ele];
            }
            return new Vector();
        }
        #endregion

        #region 依赖属性
        /// <summary>
        /// 加入到当前容器的元素的默认初始速度
        /// </summary>
        public Vector DefaultInitialVelocity
        {
            get { return (Vector)GetValue(DefaultInitialVelocityProperty); }
            set { SetValue(DefaultInitialVelocityProperty, value); }
        }

        public static readonly DependencyProperty DefaultInitialVelocityProperty =
            DependencyProperty.Register("DefaultInitialVelocity", typeof(Vector), typeof(MovementCanvas), new PropertyMetadata(default(Vector)));
        /// <summary>
        /// 设置元素在容器边缘的行为
        /// </summary>
        public EdgeBehavior EdgeBehavior
        {
            get { return (EdgeBehavior)GetValue(EdgeBehaviorProperty); }
            set { SetValue(EdgeBehaviorProperty, value); }
        }

        public static readonly DependencyProperty EdgeBehaviorProperty =
            DependencyProperty.Register("EdgeBehavior", typeof(EdgeBehavior), typeof(MovementCanvas), new PropertyMetadata(default(EdgeBehavior)));


        /// <summary>
        /// 边缘行为为“移除”时有效的边
        /// </summary>
        public EdgeType EdgeType
        {
            get { return (EdgeType)GetValue(EdgeTypeProperty); }
            set { SetValue(EdgeTypeProperty, value); }
        }

        public static readonly DependencyProperty EdgeTypeProperty =
            DependencyProperty.Register("EdgeType", typeof(EdgeType), typeof(MovementCanvas), new PropertyMetadata(EdgeType.All));



        #endregion

        #region 附加属性

        /// <summary>
        /// 获取元素的初始速度
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static Vector? GetInitialVelocity(DependencyObject obj)
        {
            return (Vector?)obj.GetValue(InitialVelocityProperty);
        }
        /// <summary>
        /// 设置元素的初始速度
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="value"></param>
        public static void SetInitialVelocity(DependencyObject obj, Vector? value)
        {
            obj.SetValue(InitialVelocityProperty, value);
        }
        /// <summary>
        /// 元素的初始速度
        /// </summary>
        public static readonly DependencyProperty InitialVelocityProperty =
            DependencyProperty.RegisterAttached("InitialVelocity", typeof(Vector?), typeof(MovementCanvas), new PropertyMetadata(null));



        public static bool GetIsPaused(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsPausedProperty);
        }

        public static void SetIsPaused(DependencyObject obj, bool value)
        {
            obj.SetValue(IsPausedProperty, value);
        }

        public static readonly DependencyProperty IsPausedProperty =
            DependencyProperty.RegisterAttached("IsPaused", typeof(bool), typeof(MovementCanvas), new PropertyMetadata(false));



        #endregion
    }
    /// <summary>
    /// 边缘的行为
    /// </summary>
    public enum EdgeBehavior
    {
        /// <summary>
        /// 反弹
        /// </summary>
        Bounce,
        /// <summary>
        /// 移除
        /// </summary>
        Remove
    }
    [Flags]
    public enum EdgeType
    {
        /// <summary>
        /// 上边
        /// </summary>
        Top = 1,
        /// <summary>
        /// 下边
        /// </summary>
        Bottom = 2,
        /// <summary>
        /// 左边
        /// </summary>
        Left = 4,
        /// <summary>
        /// 右边
        /// </summary>
        Right = 8,
        /// <summary>
        /// 所有边
        /// </summary>
        All = Top | Bottom | Left | Right,
    }
}
